package org.example.model;

import lombok.Data;
import org.apache.commons.io.IOUtils;
import org.example.exception.BizException;
import org.example.service.FileStorage;
import org.slf4j.Logger;
import org.springframework.core.io.Resource;
import org.springframework.core.io.WritableResource;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Data
public class UploadTask2 {
    private String uploadId;
    private String uploadPath;
    private final FileStorage fileStorage;
    private final Logger logger;
    private String status;
    private boolean mergeSuccess;
    private List<String> chunkFileIdList;
    private boolean cancel;
    private final Lock lock=new ReentrantLock();
    public UploadTask2(FileStorage fileStorage,String uploadPath,Logger logger){
        this.fileStorage=fileStorage;
        this.logger=logger;
        this.uploadPath=uploadPath;
        this.uploadId= UUID.randomUUID().toString();
        this.chunkFileIdList=new ArrayList<>();
        this.status="waiting";
    }
    private void saveChunk(int chunkIndex,InputStream inputStream){
        Path path=Paths.get(uploadPath,String.valueOf(chunkIndex));
        logger.debug("开始保存分片{}，目标{}",chunkIndex,path.toString());
        try (
                OutputStream outputStream=fileStorage.write(path.toString());
                ){
            IOUtils.copy(inputStream,outputStream);
            chunkFileIdList.add(path.toString());
        }catch (Exception e){
            logger.error("保存分片失败，原因：{}",e.getMessage());
            throw new BizException("保存分片失败",null);
        }
    }
    private void doMerge(List<String> chunkFileList,String mergedFilePath){
        logger.debug("开始合并，分片列表{}，目标文件{}",String.join(",",chunkFileList),mergedFilePath);
        try (
                OutputStream outputStream=fileStorage.write(mergedFilePath);
                ){
            for (String filePath:chunkFileIdList){
                if (this.cancel){
                    logger.debug("任务{}被取消",uploadId);
                    return;
                }
                InputStream inputStream=fileStorage.read(filePath);
                IOUtils.copy(inputStream,outputStream);
                inputStream.close();
            }
            logger.debug("合并分片完成，目标{}",mergedFilePath);
        }catch (Exception e){
            logger.error("任务{}合并文件时出错，原因{}",uploadId,e.getMessage());
            throw new BizException("合并文件时出错",null);
        }
    }
    private void mergeAsync(List<String> chunkFileIdList,String mergedFilePath,Runnable finallyOP){
        Thread thread=new Thread(()->{
            try {
                this.status="merging";
                doMerge(this.chunkFileIdList,mergedFilePath);
                this.mergeSuccess=true;
            }finally {
                finallyOP.run();
            }
        });
        thread.start();
    }
    public void uploadChunk(int chunkIndex, InputStream inputStream){
        if(lock.tryLock()){
            try {
                if (!this.status.equals("waiting")){
                    throw new BizException("获取锁失败或者正在合并或上传",null);
                }
                saveChunk(chunkIndex,inputStream);
            }finally {
                lock.unlock();
                this.status="waiting";
            }
        }else{
            throw new BizException("获取锁失败或者正在合并或上传",null);
        }
    }
    public void merge(){
        Thread thread=new Thread(()->{
            if(lock.tryLock()) {
                try {
                    if (!this.status.equals("waiting")) {
                        throw new BizException("获取锁失败，可能正在合并或上传", null);
                    }
                    Path mergedPath = Paths.get(uploadPath, uploadId);
                    this.status = "merging";
                    doMerge(this.chunkFileIdList, mergedPath.toString());
                    this.mergeSuccess = true;
                }finally {
                    lock.unlock();
                }
            }else{
                throw new BizException("获取锁失败，可能正在合并或上传",null);
            }
        });
        thread.start();
    }
    private void clearResource(){
        logger.debug("开始清理资源，清理列表{}",String.join(",",chunkFileIdList));
        if (lock.tryLock()){
            try {

            }catch (Exception e){
                logger.warn("清理资源失败，原因{}，清理列表{}",e.getMessage(),String.join(",",chunkFileIdList));
            }finally {
                lock.unlock();
            }
        }else {
            throw new BizException("获取锁失败，可能正在上传或合并",null);
        }
    }
    public void cancel(){
        this.cancel=true;
        Thread clearThread=new Thread(()->{
            boolean locked=false;
            try {
                locked=lock.tryLock(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (locked){
                try {
                    clearResource();
                }finally {
                    lock.unlock();
                }
            }else{
                logger.warn("未能获取清理锁");
            }
        });
        clearThread.start();
    }
    public String debugInfo(){
        return "UploadInfo{" +
                "uploadId='" + uploadId + '\'' +
                ", uploadPath='" + uploadPath + '\'' +
                ", status='" + status + '\'' +
                ", mergeSuccess=" + mergeSuccess +
                ", chunkFileIdList=" + chunkFileIdList +
                ", cancel=" + cancel +
                '}';

    }
}
